 /* AUDIO_H_ */
#include "LuaManager.h"
#include "InteractiveState.h"
#include "BPhysicsActor.h"
#include <BulletCollision\Gimpact\btBoxCollision.h>

#define SPEED 320 //~780 meters ps
using namespace Audio_Engine;
/************************************************************************/
/* CALLBACKS                                                            */
/************************************************************************/
int AudioManager::StaticEntryPoint( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData )
{
    return AudioManager::Instance()->PaStreamCallback(input,output,frameCount,timeInfo, statusFlags, userData);
}
int AudioManager::PaStreamCallback( 
    const void *input, void *output, unsigned long frameCount, 
    const PaStreamCallbackTimeInfo* timeInfo, 
    PaStreamCallbackFlags statusFlags, void *userData )
{
    /* Float32 */
    float *out = (float*)output;
    /* Prevent unused variable warning...this is not used */
    (void) input; 
    //early out if engine is off
    if(!m_active)
        return 0;
    //0 the accumulator from last frame...    
    float* leftChannel = m_lChanAccum;
    memset(leftChannel,0,sizeof(float)*m_framesPerBuffer);
    float* rightChannel =  m_rChanAccum;
    memset(rightChannel,0,sizeof(float)*m_framesPerBuffer);
    //starting at 0 samples
    float numSamplesUsed = 1.0f;
    //COPY the nodes
    {
        //This mutex can only be locked once per 4 frames in UPDATE
        ScopedCriticalSection sc(m_memoryTidyMutex);
        vNodes activeNodes;
        {
            //This one however can be locked quite often
            ScopedCriticalSection s(m_activeSourceMutex);
            activeNodes = m_activeNodes;
        }
        for(unsigned int i = 0; i < activeNodes.size(); ++i)
        {
            AudioNodeI* node = activeNodes[i];
    #ifdef _DEBUG //Quick check...this should never be hit
            if(node->m_cast_id == CAST_ID::NOT_SET)
                assert(0);
    #endif
            //Ok, so ask the node to process m_framesPerBuffer's worth of info
            numSamplesUsed = (float) i + 1;
            node->vInternalUpdate(m_framesPerBuffer);
            //Copy it into the temp buffer, then add it to the channel buffer
            memcpy(m_copyBuffer,  node->m_bLeftOut, sizeof(float) * m_framesPerBuffer);
            for(int j = 0; j < m_framesPerBuffer; ++j)
            {
                leftChannel[j] += m_copyBuffer[j];
            }
            //same for right channel
            memcpy(m_copyBuffer,  node->m_bRightOut, sizeof(float) * m_framesPerBuffer);
            for(int j = 0; j < m_framesPerBuffer; ++j)
            {
                rightChannel[j] += m_copyBuffer[j];
            }
        }
    }

    m_targetVolumeValue = numSamplesUsed;
    //output the audio data
    float mul = 1.0f / m_currentVolumeValue;
    for(int f =0; f< m_framesPerBuffer; ++f)
    {
        *out++ = leftChannel[f] * mul;
        *out++ = rightChannel[f] * mul;
    }
    return 0;
}

/************************************************************************/
/* Source Creation                                                      */
/************************************************************************/
void AudioManager::CreateSource( const char* path, const char* name, float volume)
{
    Audio_Source* s = new Audio_Source;
    SF_INFO inf;
    std::string full(path); full+=name;
    SNDFILE* test  = sf_open(full.c_str(),SFM_READ,&inf);
    sf_count_t num = inf.frames;
    s->m_buffer = new float[(unsigned int)num];
    memset(s->m_buffer, 0, sizeof(float)*(int)num);
    sf_count_t numRead = sf_read_float(test,s->m_buffer,num);
    s->m_count = (long)numRead;
    sf_close(test);
    m_sources[name] = s;
}
void AudioManager::LCreateSource( LuaPlus::LuaObject luaObj)
{
    float volume = 0.0f;
    if(luaObj.IsTable())
    {
        if(!luaObj["path"].IsString())
        {
            assert(0);
        }
        if(!luaObj["name"].IsString())
        {
            assert(0);
        }
        if(luaObj["volume"].IsNumber())
        {
            volume = (float)luaObj["volume"].GetNumber();
        }
    }
    CreateSource(luaObj["path"].GetString(), 
                 luaObj["name"].GetString(),volume);
}

/************************************************************************/
/* Lua                                                                  */
/************************************************************************/
LuaPlus::LuaObject* AudioManager::Static_MetaTable = 0;
LuaPlus::LuaObject* AudioManager::StaticInitializeLuaMetaTable()
{
    AudioManager::Static_MetaTable = new LuaPlus::LuaObject;
    *AudioManager::Static_MetaTable = LuaManager::Instance()->GetOwner()->GetGlobals().CreateTable("AudioManagerTable");
    AudioManager::Static_MetaTable->SetObject("__index", *AudioManager::Static_MetaTable);

    LUA_REGISTER("LCreateSource", AudioManager, &AudioManager::LCreateSource);
    LUA_REGISTER("LInitialiseEngine", AudioManager, &AudioManager::LInitialiseEngine);

    LUA_REGISTER("LStartAudio", AudioManager,&AudioManager::LStartAudio);
    LUA_REGISTER("LPauseAudio", AudioManager,&AudioManager::LPauseAudio);
    LUA_REGISTER("LStopAll", AudioManager,&AudioManager::LStopAll);
    LUA_REGISTER("LStopNode", AudioManager,&AudioManager::LStopNode);

    LUA_REGISTER("LCreateWAVNode", AudioManager, &AudioManager::LCreateWAVNode);
    LUA_REGISTER("LCreateDelayNode", AudioManager,&AudioManager::LCreateDelayNode);
    LUA_REGISTER("LCreatePositionNode", AudioManager,&AudioManager::LCreatePositionNode);
    LUA_REGISTER("LCreate3DSound", AudioManager,&AudioManager::LCreate3DSound);
    return AudioManager::Static_MetaTable;
}

/************************************************************************/
/* StartUp, ShutDown, Update                                            */
/************************************************************************/
void AudioManager::LInitialiseEngine( int framesPerBuffer, int channels, 
                                      int sampleRate, float maxDeley,int maxSounds)
{
    m_framesPerBuffer = framesPerBuffer;
    m_channels = channels;
    m_sampleRate = sampleRate;
    m_maxDeleyTime = maxDeley;
    m_currentVolumeValue = 1.0f;
    m_targetVolumeValue = 1.0f;
    int numDeleyedSamples = (int)((float)m_sampleRate * (m_maxDeleyTime));
    Delay_Node::sDelayMax = numDeleyedSamples;
    //Create and 0 the buffers
    m_lChanAccum = new float[m_framesPerBuffer];
    m_rChanAccum = new float[m_framesPerBuffer];
    m_copyBuffer = new float[numDeleyedSamples];

    memset(m_lChanAccum, 0, sizeof(float)*m_framesPerBuffer);
    memset(m_rChanAccum, 0, sizeof(float)*m_framesPerBuffer);
    memset(m_copyBuffer, 0, sizeof(float)*numDeleyedSamples);
    {
        ScopedCriticalSection s(m_activeSourceMutex);
        m_activeNodes.reserve(maxSounds);
    }
    // init PA
    Pa_Initialize();

    //an error handler
    PaError err;

    // output params
    PaStreamParameters outputParameters;
    outputParameters.device = Pa_GetDefaultOutputDevice();
    outputParameters.channelCount = m_channels;
    outputParameters.sampleFormat = paFloat32;
    outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    /* Open an audio I/O stream. */
    err = Pa_OpenStream( 
        &m_stream,                      /* the stored handle */
        NULL,                           /* no input params */
        &outputParameters,              /* output params */
        m_sampleRate,                   /* sample rate */
        m_framesPerBuffer,              /* frames per buffer */
        paClipOff,                      /* flags */
        &AudioManager::StaticEntryPoint,                 /* this is the callback function */
        this);                        /* this is a pointer that will be passed to the callback*/

    //Geom
    m_geomManager = new Geometry_Manager;
}
void AudioManager::Update()
{
    if(!m_active)
        return;
    unsigned int size = 0;
    //CONCURRENCY
    {
        ScopedCriticalSection s(m_activeSourceMutex); 
        size = m_activeNodes.size();
        //remove inactive nodes from the actve queue
        for(vNodes::iterator it = m_activeNodes.begin(); 
            it!= m_activeNodes.end();)
        {
            if(!(*it)->m_active)
            {
                it = m_activeNodes.erase(it);
            }
            else
            {
                (*it)->vExternalUpdate();
                ++it;
            }
        }
    }
    if(size == 0)
    {
        int maxDeletesPerFrame = 15;
        int currentDelets = 0;
        //Stop and remove the inactive ones from memory
        for(unsigned int i = 0; i < m_allNodes.size(); ++i)
        {
            if(currentDelets > maxDeletesPerFrame)
                break;
            if( m_allNodes[i]->m_active== false)
            {
                if(!IsNodeInActive(m_allNodes[i]->m_audio_id))
                {
                    StopSample(i);
                    currentDelets++;
                    i = 0;
                }
            }
        }
    }
    m_currentVolumeValue = Vector::lerp(m_currentVolumeValue, m_targetVolumeValue, Engine::Instance()->m_lastFrameDt / 1000.0f);

    m_frameCounter++;
    if(m_frameCounter > 4)
    {
        //4 frames have gone by, lock the memory tidy mutex
        //and force a delete
        ScopedCriticalSection sc(m_memoryTidyMutex);
        int maxDeletesPerFrame = 15;
        int currentDelets = 0;
        //Stop and remove the inactive ones from memory
        for(unsigned int i = 0; i < m_allNodes.size(); ++i)
        {
            if(currentDelets > maxDeletesPerFrame)
                break;
            if( m_allNodes[i]->m_active== false)
            {
                if(!IsNodeInActive(m_allNodes[i]->m_audio_id))
                {
                    StopSample(i);
                    currentDelets++;
                    i = 0;
                }
            }
        }
        m_frameCounter = 0;
    }
}
void AudioManager::ShutDown()
{
    m_geomManager->CleanUp();
    delete m_geomManager;
    Pa_StopStream( m_stream );
    Pa_CloseStream( m_stream );

    Pa_Terminate();
    {
        ScopedCriticalSection s(m_activeSourceMutex);
        m_activeNodes.clear();
    }
    unsigned int num = m_allNodes.size();
    for(unsigned int i = 0; i < num; ++i)
    {
        StopSample(0); //0 on purpose,
    }
    for(mSources::iterator it = m_sources.begin();
        it !=m_sources.end();
        ++it)
    {
        delete[] it->second->m_buffer;
        delete it->second;
    }
    m_sources.clear();
    delete[] m_lChanAccum;
    delete[] m_rChanAccum;
    delete[] m_copyBuffer;
}

//individual samples
void AudioManager::LStopNode( audio_id index)
{
    StopNode(index);
}
void AudioManager::StopNode( audio_id index )
{
    for(unsigned int i = 0; i < m_allNodes.size(); ++i)
        if(m_allNodes[i]->m_audio_id == index)
            m_allNodes[i]->m_active = false;
}

//engine utils
void AudioManager::LPauseAudio()
{
    PauseAudio();
}
void AudioManager::PauseAudio()
{
    if(m_stream)
    {
        m_active = false;
        Pa_StopStream(m_stream);
    }
}
void AudioManager::StartAudio()
{
    if(m_stream)
    {
        m_active = true;
        Pa_StartStream( m_stream );
    }
}
void AudioManager::LStartAudio()
{
    StartAudio();
}
void AudioManager::LStopAll()
{
    {
        ScopedCriticalSection s(m_activeSourceMutex);
        m_activeNodes.clear();
    }
    for(unsigned int i = 0; i <m_allNodes.size(); ++i)
    {
        delete m_allNodes[i];
    }
    m_allNodes.clear();
}
AudioNodeI* AudioManager::GetNode( audio_id which )
{
    for(unsigned int i = 0; i < m_allNodes.size(); ++i)
        if(m_allNodes[i]->m_audio_id == which)
            return m_allNodes[i];

    return NULL;
}
float AudioManager::CalculateTimeToListener( const Vec3& start )
{
    Vec3 toSound = m_listener->vGetPosition() - start;
    float dist = toSound.magnitude();
    float time  = dist / SPEED; //haha LOL
    if(time > m_maxDeleyTime)
        return m_maxDeleyTime;
    return time;
}
bool AudioManager::IsNodeInActive( audio_id id )
{
    {
        ScopedCriticalSection s(m_activeSourceMutex);
        for(unsigned int i = 0; i < m_activeNodes.size(); ++i)
            if(m_activeNodes[i]->m_audio_id == id)
                return true;
    }
    return false;
}
void AudioManager::StopSample( int index )
{
    assert(index <(int) m_allNodes.size());
    vNodes::iterator it = m_allNodes.begin() + index;
    AudioNodeI* s = *it;
    m_allNodes.erase(it);
    s->Terminate(); //Safe to do this here
    delete s;
}
void Audio_Engine::AudioManager::SafeAddNode( AudioNodeI* node )
{
    {
        ScopedCriticalSection s(m_activeSourceMutex);
        m_activeNodes.push_back(node);
    }
    m_allNodes.push_back(node);
}

//WAVS
CAST_ID::cast_id Wav_Player::sCastID = CAST_ID::WAV_PLAYER;
audio_id AudioManager::LCreateWAVNode( const char* sourceName, bool loop, float fadeIn, float fadeOut )
{
    return CreateWAVNode(std::string(sourceName), loop, fadeIn, fadeOut);
}
audio_id AudioManager::CreateWAVNode( const std::string& sourceName, bool loop, float fadeIn, float fadeOut  )
{
    mSources::iterator it = m_sources.find(sourceName);
    if(it == m_sources.end())
        return -1;

    Wav_Player* w = new Wav_Player;
    w->m_active = true;
    w->m_audio_id = GenerateNewID();
    w->SetAmplitude(1.0f);
    w->SetLoop(loop);
    w->SetSource(it->second);
    w->SetUpLoopBack();
    w->SetFades(fadeIn,fadeOut);
    SafeAddNode(w);
    return w->m_audio_id;
}
void Wav_Player::vExternalUpdate()
{}
void Wav_Player::vInternalUpdate(int blockSize)
{
    for(int i = 0; i < blockSize; ++i)
    {
        //FADE in if necessesary
        if(m_index < m_fadeIN) //HARD CODED, fade all wavs in over 0.1 seconds (crap!)
        {
            m_amplitude = ( (float)m_index / m_fadeIN);
        }
        else if(m_index > m_source->m_count - m_fadeOUT)
        {
            m_amplitude = (((float)m_source->m_count - (float)m_index) / m_fadeOUT );
        }
        else
            m_amplitude = 1.0f;
        float outPut = m_source->m_buffer[m_index] * m_amplitude ;//* m_bInbus[i];
        m_index++;
        if(m_index > m_source->m_count)
        {
            if(!m_loop)
            {
                OutPutZero(blockSize);
                m_active = false;
                return;
            }
            else
                m_index = 0;
        }

        //int test = m_index % m_source->m_count;
        //m_index &= test; //No branching! Hurray!
        
        m_bOutput[LEFT_CHAN][i] = outPut;
        m_bOutput[RIGHT_CHAN][i] = outPut;
        if(m_outPut)
        {
            m_bLeftOut[i] = m_bOutbus[LEFT_CHAN][i];
            m_bRightOut[i] = m_bOutbus[RIGHT_CHAN][i];        
        }
    }
}
void Wav_Player::StopOutput()
{
    m_outPut = false;
    OutPutZero(AudioManager::Instance()->GetFramesPerBuffer());
}

//DELAYS
CAST_ID::cast_id Delay_Node::sCastID = CAST_ID::DELAY_NODE;
int Delay_Node::sDelayMax = 0;
audio_id AudioManager::CreateDelayNode( audio_id which, float secs )
{
    AudioNodeI* target = GetNode(which);
    if(target != NULL)
    {
        Delay_Node* d = new Delay_Node;
        d->m_active = true;
        d->CreateDelay(secs, target);
        SafeAddNode(d);
        return d->m_audio_id;
    }
    return -1;
}
audio_id AudioManager::LCreateDelayNode( audio_id which, float secs )
{
    return CreateDelayNode(which, secs);
}
void Delay_Node::vInternalUpdate(int frames)
{
    bool input = true;
    if(m_delayNotification <= 0 && m_source && !m_source->m_active)
    {
        m_delayNotification++;
    }

    if(m_delayNotification > 0)
    {
        input = false;
        //Triggered quit, play the last loop and then quit
        if(m_delayNotification > m_actualDelay)
        {
            //these will be played so 0 them,
            OutPutZero(frames);
            m_active = false;
            return;
        }
        m_delayNotification += frames;
        m_source = NULL;
    }
    for(int i = 0; i < frames; ++i)
    {
        //Position the Read and Write pointers
        if( m_delayWrite >=/* important = !*/ Delay_Node::sDelayMax)
            m_delayWrite = 0;
        m_delayRead = m_delayWrite - m_actualDelay;
        if( m_delayRead< 0 ) 
            m_delayRead += Delay_Node::sDelayMax;

        //Receive our inputs
        if(input)
        {
            m_bInbus[LEFT_CHAN][i] = m_bInput[LEFT_CHAN][i];
            m_bInbus[RIGHT_CHAN][i] = m_bInput[RIGHT_CHAN][i];

            //Right the inputs out to the d\elay buffers
            m_delayBuffer[LEFT_CHAN][m_delayWrite] = m_bInbus[LEFT_CHAN][i];
            m_delayBuffer[RIGHT_CHAN][m_delayWrite] = m_bInbus[RIGHT_CHAN][i];
        }
                    
        //Output our delayed sounds
        m_bOutput[LEFT_CHAN][i] = ((m_delayBuffer[LEFT_CHAN][m_delayRead]));//+ d->inbus )* 0.5f; 
        m_bOutput[RIGHT_CHAN][i] = ((m_delayBuffer[RIGHT_CHAN][m_delayRead]));//+ d->inbus )* 0.5f; 

        //Output what is on our outbus's
        m_bLeftOut[i] = m_bOutbus[LEFT_CHAN][i];
        m_bRightOut[i] = m_bOutbus[RIGHT_CHAN][i];

        m_delayWrite += 1;
    }   
}
void Delay_Node::CreateDelay( float seconds, AudioNodeI* target )
{
    m_source = target;
    m_actualDelay = (int)(seconds * (float)AudioManager::Instance()->GetSampleRate());
    if(m_actualDelay > sDelayMax)
        m_actualDelay = sDelayMax;
    m_targetDelay = m_actualDelay;
    m_delayBuffer[LEFT_CHAN] = new float[Delay_Node::sDelayMax];
    m_delayBuffer[RIGHT_CHAN] = new float[Delay_Node::sDelayMax];
    memset(m_delayBuffer[LEFT_CHAN],0,sizeof(float)*Delay_Node::sDelayMax);
    memset(m_delayBuffer[RIGHT_CHAN],0,sizeof(float)*Delay_Node::sDelayMax);
    m_delayRead = 0;
    m_delayWrite = 0;
    m_active = true;
    m_bInput[LEFT_CHAN] = target->GetOutBus(LEFT_CHAN);
    m_bInput[RIGHT_CHAN] = target->GetOutBus(RIGHT_CHAN);
    m_bOutput[LEFT_CHAN] = m_bOutbus[LEFT_CHAN];
    m_bOutput[RIGHT_CHAN] = m_bOutbus[RIGHT_CHAN];
    target->AddNotification(&m_delayNotification);
}
void Delay_Node::vExternalUpdate()
{
    //HACKED LOL...seriously...kind of hacky...but the sound is MUCH improved.
    if(m_targetDelay != m_actualDelay)
    {
        //TO DO: Profile this bad boy
        float start = m_actualDelay / (float)AudioManager::Instance()->GetSampleRate();
        float end = m_targetDelay / (float)AudioManager::Instance()->GetSampleRate();
        float n = Vector::lerp(start, end, Engine::Instance()->m_lastFrameDt / 4000.0f); //One quater
        m_actualDelay = (int)(n * (float)AudioManager::Instance()->GetSampleRate());
    }
}

//POSITIONS
CAST_ID::cast_id Position_Node::sCastID = CAST_ID::POSITON;
void Position_Node::vInternalUpdate( int blockSize )
{
    if(m_sourceNotifier > 0 || (m_source && !m_source->m_active))
    {
        m_source = NULL;
        m_sourceNotifier++;
        //these will be played so 0 them,
        OutPutZero(blockSize);
        m_active = false;
        return;
    }
    for(int i = 0; i < blockSize; ++i)
    {
        m_bInbus[LEFT_CHAN][i] = m_bInput[LEFT_CHAN][i];
        m_bInbus[RIGHT_CHAN][i] = m_bInput[RIGHT_CHAN][i];
        float leftOut = m_bInbus[LEFT_CHAN][i] * m_distanceAtten;
        float rightOut = m_bInbus[RIGHT_CHAN][i] * m_distanceAtten;
        m_bOutput[LEFT_CHAN][i] = m_pan[LEFT_CHAN] * leftOut;
        m_bOutput[RIGHT_CHAN][i] = m_pan[RIGHT_CHAN] * rightOut;
        m_bLeftOut[i] =  m_bOutbus[LEFT_CHAN][i];
        m_bRightOut[i] = m_bOutbus[RIGHT_CHAN][i];
    }
}
void Position_Node::vExternalUpdate()
{
    //Adjust volume based on position to listener

    Audio_Listener* l = AudioManager::Instance()->GetListener();
    if(l == NULL)
        return;

    Vec3 toSound = l->vGetPosition() - m_position;
    toSound.normalise();
    Vec3 up = l->vGetUp();
    Vec3 right = l->vGetDirection().CrossProduct(up);
    float dotProduct = toSound.DotProduct(right);
    //Pan is now 0 to 1
    float pan = 1.0f - abs((1.0f + dotProduct) * 0.5f);
    m_pan[0] = sin((pan*90) * (float)M_PI / 180.0f);
    m_pan[1] = cos((pan*90) * (float)M_PI / 180.0f);  

    float dist = 0.0f;
    m_shape->UpdateTranslation(m_position);
    if(m_shape->CollidesWithListener(l, &dist ))
    {
        float maxDist = l->vGetRadiusSquared() + 
            m_radiusSquared;
        m_distanceAtten = 1.0f - (dist / maxDist);
    }
    
}
void Position_Node::CreatePosition( AudioNodeI* target )
{
    m_source = target;
    for(int i = 0; i < AudioManager::Instance()->GetFramesPerBuffer(); ++i)
        m_bInbus[LEFT_CHAN][i] = m_bInbus[RIGHT_CHAN][i] = 1.0f;
    Audio_Sphere_Shape* s = new Audio_Sphere_Shape;
    s->SetRadiusSquared(m_radiusSquared);
    m_shape = s;
    m_bInput[LEFT_CHAN] = target->GetOutBus(LEFT_CHAN);
    m_bInput[RIGHT_CHAN] = target->GetOutBus(RIGHT_CHAN);
    m_bOutput[LEFT_CHAN] = m_bOutbus[LEFT_CHAN];
    m_bOutput[RIGHT_CHAN] = m_bOutbus[RIGHT_CHAN];
    target->AddNotification(&m_sourceNotifier);
}
audio_id AudioManager::LCreatePositionNode( audio_id which, float radius , bool stopSource, float x, float y, float z)
{
    return CreatePositionNode(which, radius, stopSource, x,y,z);
}
audio_id AudioManager::CreatePositionNode( audio_id which, float radius , bool stopSource, float x, float y, float z)
{
    AudioNodeI* target = GetNode(which);
    if(target != NULL)
    {
        //Create the node
        Position_Node* p = new Position_Node;
        //If the source is a WAV
        if(target->m_cast_id &&  stopSource)
        {
            Safe_Caster<Wav_Player> sc;
            Wav_Player* w = sc.SafeCast(target);
            if(w != NULL)
            {
                //IMPORTANT
                w->StopOutput();
            }
        }
        p->m_active = true;
        p->SetPosition(Vec3(x,y,z));
        p->SetRadiusSquared(radius*radius);
        p->CalculateAABBFromSphere();
        p->CreatePosition(target);
        SafeAddNode(p);
        return p->m_audio_id;
    }
    return -1;
}
Position_Node::~Position_Node()
{
    if(m_shape)
        delete m_shape;
}

//Echoes
CAST_ID::cast_id Echo_Node::sCastID = CAST_ID::ECHO;
audio_id AudioManager::LCreate3DSound(const char* name, float x, float y, float z, float radius)
{
    return Create3DSound(std::string(name), Vec3(x,y,z), radius);
}
audio_id AudioManager::Create3DSound(const std::string& sourceName, Vec3 position, float radius)
{
    // Create wav source
    audio_id wavID = CreateWAVNode(sourceName, false, 0.1f, 0.01f);
    Safe_Caster<Wav_Player> scWAV;
    Wav_Player* wP = scWAV.SafeCast(GetNode(wavID));
    if(!wP)
        return -1;

    // Position the wav source, stop outputing the wav
    audio_id posID = CreatePositionNode(wavID, radius, true, position.m_x,position.m_y,position.m_z );
    Safe_Caster<Position_Node> scPOS;
    Position_Node* pN = scPOS.SafeCast(GetNode(posID));
    if(!pN)
        return -1;
    //For each collision with the position node, add an echo
    vAudioCols collisions;
    m_geomManager->CheckCollisions(collisions, pN);
    for(unsigned int i = 0; i < collisions.size(); ++i)
    {
        //new an echo node...
        Echo_Node* n = new Echo_Node;

        //Calcuate it's position based on the center of the collidint triangle
        Audio_Triangle* t = &collisions[i].which->m_tris[collisions[i].triIndex];
        Vec3 averagePos;
        for(int j = 0; j < 3; ++j)
            averagePos += t->m_verts[j];
        averagePos *= (1.0f / 3.0f);
        n->SetPosition(averagePos);

        //Create a position node to allow for attenuation and panning
        Position_Node* node = new Position_Node;
        node->SetRadiusSquared(collisions[i].which->GetRadiusSquared() /*+ pN->GetRadiusSquared()*/);
        node->SetPosition(averagePos);
        node->CalculateAABBFromSphere();

        //And finally Create a collision shape for it
        Audio_HemiSphere_Shape* s = new Audio_HemiSphere_Shape;
        s->SetUp(averagePos, t->m_norm, collisions[i].which->GetRadiusSquared() + 2 * pN->GetRadiusSquared());
//         Audio_Sphere_Shape* s = new Audio_Sphere_Shape;
//         s->UpdateTranslation(averagePos);
//         s->SetRadiusSquared( collisions[i].which->GetRadiusSquared() );

        //now pass it in to create echo
        n->CreateEcho(wP, pN, node, s);
        n->m_active = true;
        SafeAddNode(n);
    }

    return pN->m_audio_id;
}
void Echo_Node::CreateEcho( Wav_Player* sound, Position_Node* source, 
                            Position_Node* myNode, Audio_Collision_Shape* shape)
{
    m_bOutput[LEFT_CHAN] = m_bOutbus[LEFT_CHAN];
    m_bOutput[RIGHT_CHAN] = m_bOutbus[RIGHT_CHAN];
    m_source = source;
    m_sourceToSelf = new Delay_Node;
    m_selfToListenDelay = new Delay_Node;

    //This delay is based on the distance between us and the source
    float dist0 = (m_pos - m_source->GetPosition()).magnitude();
    float time0  = dist0 / SPEED; //haha LOL
    m_sourceToSelf->CreateDelay(time0, sound);

    //so we know when this goes away
    sound->AddNotification(&m_sourceNotifier);

    //This delay is based on the distance between us and the listener
    float dist1 = (m_pos - AudioManager::Instance()->GetListener()->vGetPosition()).magnitude();
    float time1  = dist1 / SPEED; //haha LOL
    m_selfToListenDelay->CreateDelay(time1, m_sourceToSelf);

    //make sure position node affect sthe righr delay!
    myNode->CreatePosition(m_selfToListenDelay);
    m_pNode = myNode;
    //and store the collision shape to go with it
    m_shape = shape;
}
void Echo_Node::vExternalUpdate()
{
    float dist = 0.0f;
    //If our overall collision shape hits the listener
    if(m_shape->CollidesWithListener(AudioManager::Instance()->GetListener(), &dist))
    {
        /* Update delays and position node as normal*/
        //This delay is based on the distance between us and the source
        if(m_sourceToSelf && m_sourceNotifier <= 0)
        {
            float dist0 = (m_pos - m_source->GetPosition()).magnitude();
            float time0  = dist0 / SPEED;
            int delay0 = (int)((float)AudioManager::Instance()->GetSampleRate() * time0);
            if(delay0 > Delay_Node::sDelayMax)
                delay0 = Delay_Node::sDelayMax;
            m_sourceToSelf->UpdateDelayValue(delay0);
            m_sourceToSelf->vExternalUpdate();
        }

        //This delay is based on the distance between us and the listener
        if(m_selfToListenDelay)
        {
            float dist1 = (m_pos - AudioManager::Instance()->GetListener()->vGetPosition()).magnitude();
            float time1  = dist1 / SPEED;
            int delay1 = (int)((float)AudioManager::Instance()->GetSampleRate() * time1);
            if(delay1 > Delay_Node::sDelayMax )
                delay1 = Delay_Node::sDelayMax;
            m_selfToListenDelay->UpdateDelayValue(delay1);
            m_selfToListenDelay->vExternalUpdate();
        }   

        if(m_pNode)
            m_pNode->vExternalUpdate();
    }
    else
    {
        //Other whys, force silence!
        m_pNode->SetAttenuation(0.0f);
    }
}
void Echo_Node::vInternalUpdate( int frames )
{
    // Get the data output by the wav file, delay it
    // by the time it takes to get to our position
    m_sourceToSelf->vInternalUpdate(frames);
    if(!m_sourceToSelf->m_active)
    {
        m_sourceToSelf->ForceNotifications();
    }

    //Now delay the output of the previous delay,
    //by the time it takes for it to reach the listener
    m_selfToListenDelay->vInternalUpdate(frames);
    if(!m_selfToListenDelay->m_active)
    {
        m_selfToListenDelay->ForceNotifications();
        OutPutZero(frames);
        m_active = false;
        return;
    }

    m_pNode->vInternalUpdate(frames);
    for(int i = 0; i < frames; ++i)
    {
        m_bOutput[LEFT_CHAN][i] = m_pNode->m_bLeftOut[i];
        m_bOutput[RIGHT_CHAN][i] = m_pNode->m_bRightOut[i];
        m_bLeftOut[i] =  m_bOutbus[LEFT_CHAN][i];
        m_bRightOut[i] = m_bOutbus[RIGHT_CHAN][i];
    }
}
Audio_Engine::Echo_Node::~Echo_Node()
{
    if(m_sourceToSelf)
    {
        delete m_sourceToSelf;
    }
    if(m_selfToListenDelay)
    {
        delete m_selfToListenDelay;
    }
    if(m_pNode)
    {
        m_pNode->Terminate();
        delete m_pNode;
    }
    if(m_shape)
        delete m_shape;
}

//Geometry
bool IsPointInSphere( const Vec3& spherePos, float radSQR, const Vec3& point)
{
    Vec3 toNode = spherePos - point;
    float dist = sqrtf(toNode.squareMagnitude());
    float radius0 = sqrtf(radSQR);
    return dist < radius0;
}
void Geometry_Manager::CheckCollisions( vAudioCols& cols, Position_Node* pN )
{
    //INITIAL sphere sphere testing
    audio_cash_set cacheSet;
    for( vAudioGeom::iterator it = m_managedGeom.begin();
        it != m_managedGeom.end();
        ++it) //for each geom Node
    {
        //Check the distance between the sonic boundary and
        //the geometry  
        Vec3 toNode = pN->GetPosition() - (*it)->GetPosition();
        float dist = sqrtf(toNode.squareMagnitude());
        float radius0 = sqrtf(pN->GetRadiusSquared());
        float radius1 = sqrtf((*it)->GetRadiusSquared());
        if(dist <  radius0+radius1 )
        {
            //We have a sphere-sphere collision,
            //Store them in order of the distance
            Collision_Cache c;
            c.dist = dist;
            c.which = (*it);
            cacheSet.insert(c);
        }
    }
    //Now per triangle testing for the closest building
    int CollisionCount = 0;
    int CollisionMax = 15; //15 per source
    int CollisionPBCount = 0;
    int CollisionsPerBuilding = 5; //5 per building.
    for(audio_cash_set::iterator it = cacheSet.begin();
        it != cacheSet.end();
        ++it) 
    {
        CollisionPBCount = 0;
        Audio_Geometry* geom = it->which;
        Vec3 toGeom = pN->GetPosition() - geom->GetPosition();
        toGeom.normalise();
        for(unsigned int i = 0; i < geom->m_tris.size(); ++i)
        {
            if(CollisionPBCount >= CollisionsPerBuilding)
                break;
            if(toGeom.DotProduct(geom->m_tris[i].m_norm) < 0.0f) //If tri is facing right way
            {
                for(int point = 0; point < 3; ++point)
                {
                    if(CollisionCount > CollisionMax) //eurgh
                        return;
                    if(IsPointInSphere(pN->GetPosition(), pN->GetRadiusSquared(),
                        geom->m_tris[i].m_verts[point])) //if the triangle has a point inside the sphere
                    {
                        //New collision!
                        Audio_Collision ac;
                        ac.which = geom;
                        ac.triIndex = i;
                        ac.normal = geom->m_tris[i].m_norm;
                        cols.push_back(ac);
                        CollisionCount++;
                        CollisionPBCount++;
                        break; //only one per tri
                    }
                }
            }
        }
    }
}
void Audio_Geometry::CreateSphereBVFromMesh( DirectXMesh* mesh, float scale)
{
    D3DXVECTOR3 total(0,0,0);
    int count = mesh->Verts().size();
    int verts[3] = {0,0,0};
    //for each vert
    for(unsigned int i = 0; i < mesh->Verts().size(); ++i)
    {
        total += mesh->Vert(i);
    }
    //average the total
    D3DXVec3Scale(&total, &total, scale);
    total *= ( 1.0f / count);

    //work out radius
    float maxDistance = 0.0f;
    //for each triangle
    //for each vert
    for(unsigned int i = 0; i < mesh->Verts().size(); ++i)
    {
        D3DXVECTOR3 toVert = total - mesh->Verts()[i];
        float sd = D3DXVec3LengthSq(&toVert);
        if(sd > maxDistance)
            maxDistance = sd;
    }
    m_radiusSquared = maxDistance;
    m_centerPosition.SetStats(total.x,total.y,total.z);
}
void Audio_Geometry::GenerateGeometryFromActor( BPhysicsActor* actor, float scale )
{
    //Steal the bounding sphere and box from thr  Bullet code
    btVector3 min, max;
    actor->GetBodyPointer()->getAabb(min, max);
    m_aabbMin.SetStats(min.x(), min.y(), min.z());
    m_aabbMax.SetStats(max.x(), max.y(), max.z());
    btVector3 center; float radius;
    actor->GetBodyPointer()->getCollisionShape()->getBoundingSphere(center,radius);
    m_centerPosition.SetStats(center.x(), center.y(), center.z());
    m_radiusSquared = radius * radius;
    //Now add the triangles
    DirectXMesh* m = actor->GetMesh();
    for(unsigned int i = 0; i < m->Triangles().size(); ++i)
    {
        Audio_Triangle t;
        int index[3] = {0,0,0};
        for(int k =0; k < 3; ++k)
            index[k] = m->Triangles()[i].m_vertIndex[k];
        for(int k =0; k < 3; ++k)
        {
            D3DXVECTOR3 store = m->Vert(index[k]);
            D3DXVec3Scale(&store, &store, scale);
            t.m_verts[k].SetStats(store.x,store.y,store.z);
        }
        t.m_norm = Vector::CalculateNormalTri(t.m_verts[1],t.m_verts[0],t.m_verts[2]);
        m_tris.push_back(t);
    }
}

//SPHERES
bool Audio_Sphere_Shape::CollidesWithListener( Audio_Listener* l, float* dist )
{
    //Distance Attenuation
    if(m_radSquared > 0)
    {
        float maxDist = l->vGetRadiusSquared() + 
                        m_radSquared;
        float currentDist = (l->vGetPosition() - 
                             m_pos).squareMagnitude();
        if( currentDist < maxDist)
        {
            *dist = currentDist;
            return true;
        }
    }
    *dist = 0.0f;
    return false;
}
void Audio_Sphere_Shape::DebugDrawShape(const Vec3& color)
{
    Engine::Instance()->GetCurrentState()->DebugDrawSphere(m_pos, sqrtf(m_radSquared),color, true);
}
//HEMISPHERES
void Audio_HemiSphere_Shape::SetUp( const Vec3& pos, const Vec3& normal, float radSQR )
{
    m_position = pos; m_radiusSquared = radSQR;
    /*We can work out 3 points on our plane easily enough...
      Point one is our origin...
      Then we can work out a vector parallel to out plane using the cross product
      with this vector, we can find two points that lie on the plane
    */
    Vec3 planeVecs[3];
    planeVecs[0] = m_position;

    Vec3 upGuess(0,1,0);
    if(normal.m_y > 0.9)
        upGuess.SetStats(0,0,1);

    Vec3 right = normal.CrossProduct(upGuess);
    right.normalise();

    Vec3 actualUp = right.CrossProduct(normal);
    actualUp.normalise();

    planeVecs[1] = m_position + right * m_radiusSquared;
    planeVecs[2] = m_position + actualUp * m_radiusSquared;

    m_plane.CreateFromPoints(planeVecs[0],planeVecs[1],planeVecs[2]);
}
bool Audio_HemiSphere_Shape::CollidesWithListener( Audio_Listener* l, float* dist )
{
    //Distance Attenuation
    if(m_radiusSquared > 0)
    {
        float maxDist = l->vGetRadiusSquared() + 
                        m_radiusSquared;
        float currentDist = (l->vGetPosition() - 
                             m_position).squareMagnitude();
        if( currentDist < maxDist) //Collides with sphere, but is it on the right side of the plane?
        {
            if(m_plane.Distance(l->vGetPosition()) > 0)
            {
                *dist = currentDist;
                return true;
            }
        }
    }
    *dist = 0.0f;
    return false;
}
void Audio_HemiSphere_Shape::DebugDrawShape(const Vec3& color)
{
    Engine::Instance()->GetCurrentState()->DebugDrawHemiSphere(m_position, sqrtf(m_radiusSquared), m_plane.GetNormal(),color, false);
}
